package com.zzyl.security;

import cn.hutool.core.text.AntPathMatcher;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.zzyl.constant.Constants;
import com.zzyl.constant.UserCacheConstant;
import com.zzyl.properties.JwtTokenManagerProperties;
import com.zzyl.utils.JwtUtil;
import com.zzyl.vo.UserAuth;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.function.Supplier;

/**
 * @Description JwtAuthorizationManager
 * @Author luohai
 * @Date 2024-10-29
 */
@Component
public class JwtAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {
    @Autowired
    private JwtTokenManagerProperties jwtTokenManagerProperties;

    @Autowired
    private StringRedisTemplate redisTemplate;


    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext rac) {
        //1.校验token
        //获取请求对象request
        HttpServletRequest request = rac.getRequest();
        //获取请求头token
        String token = request.getHeader(Constants.USER_TOKEN);
        if (StrUtil.isEmpty(token)){
            return new AuthorizationDecision(false);
        }

        //解析token得到载荷userVo的json字符串
        Claims claims = JwtUtil.parseJWT(jwtTokenManagerProperties.getBase64EncodedSecretKey(), token);
        String json = claims.get("currentUser").toString();
        //获取用户数据UserAuth并封装到springSecurity上下文，注明已认证
        //将json字符串转换为userAuth
        UserAuth userAuth = JSONUtil.toBean(json, UserAuth.class);
        //封装已认证用户对象UsernamePasswordAuthenticationToken
        UsernamePasswordAuthenticationToken upat = new UsernamePasswordAuthenticationToken(userAuth, userAuth.getPassword(), null);
        //封装springSecurity上下文
        SecurityContextHolder.getContext().setAuthentication(upat);

        //3.鉴权操作
        //获取用户当前访问路径path
        String path = request.getRequestURI();
        //获取当前用户的请求方式
        String method = request.getMethod();
        //拼接当前访问完整路径
        path = method + path;
        //从redis中获取可访问的资源路径列表list<string>；每个路径格式；请求方式/资源路径
        String json2 = redisTemplate.opsForValue().get(UserCacheConstant.ACCESS_URLS_CACHE + userAuth.getId());
        List<String> urls = JSONUtil.toList(json2, String.class);
        //遍历资源路径列表判断是否与当前访问的完整路径匹配，匹配就返回鉴权成功
        for (String url : urls) {
            if (antPathMatcher.match(url,path)){
                //匹配上了，返回鉴权成功
                return new AuthorizationDecision(true);
            }
        }
        //来到这里说明鉴权失败
        return new AuthorizationDecision(false);
    }
}
